using System;
using System.Collections.Generic;
using System.Reflection;
using SimpleInjector;
using Sustainalytics.Entities;
using Sustainalytics.Entities.Governance;
using Sustainalytics.Synchronization.Interface;
using Sustainalytics.Synchronization.Validation.Internals;
using Sustainalytics.Synchronization.Validation.Restore;
using Sustainalytics.Synchronization.Validation.ValidationRules.Common;
using Sustainalytics.ESG.Entities;

namespace Sustainalytics.Synchronization.Validation
{
    public static class ProfileRestoreCompositionRoot
    {
        private static readonly List<Type> _allreadyRegistredTypes = new List<Type>();

        private static readonly Dictionary<BusinessResearchProduct, List<Type>> ResearchTypeDictionary =
            new Dictionary<BusinessResearchProduct, List<Type>>
                {
                    { BusinessResearchProduct.Core, new List<Type> {
                        typeof(RPCompanyProfile),
                        typeof(RPIndicatorExtended),
                        typeof(RPCompanyESGComputationResult),
                        typeof(CompanyKeyESGIssue),
                        typeof(CompanyWeights),
                        typeof(EsgResearchProfile) } },
                    { BusinessResearchProduct.Comprehensive, new List<Type> {
                        typeof(RPCompanyProfile),
                        typeof(RPIndicatorExtended),
                        typeof(RPCompanyESGComputationResult),
                        typeof(CompanyKeyESGIssue),
                        typeof(Source),
                        typeof(CompanyWeights) } },
                    { BusinessResearchProduct.CorporateGovernance, new List<Type> {
                        typeof(CorporateGovernanceResearchProfile),
                        typeof(GovernanceComputationResult),
                        typeof(GovernanceIssue),
                        typeof(GovernanceSource),
                        typeof(GovernanceIndicator),
                        typeof(GovernanceCompanyProfile) } }
                };

        public static Container Register(SyncContext context)
        {
            var container = new Container();
            container.RegisterAllCollections(context.LastGAConnectionStrings, context.GAConnectionStrings);
            container.RegisterDataHandlers();
            container.RegisterLoggers(context);
            container.Register(() => new ProfileRestoreEngine(container.GetAllInstances<IProfileRestoreHandler>()));
            container.Verify();
            return container;
        }

        private static void RegisterAllCollections(
            this Container container,
            Dictionary<string, string> currentConnectionStrings,
            Dictionary<string, string> syncConnectionStrings)
        {
            RegisterCollections(container, currentConnectionStrings, syncConnectionStrings, BusinessResearchProduct.Comprehensive, x => x["ClientDW"]);
            RegisterCollections(container, currentConnectionStrings, syncConnectionStrings, BusinessResearchProduct.Core, x => x["ClientDW"]);
            RegisterCollections(container, currentConnectionStrings, syncConnectionStrings, BusinessResearchProduct.CorporateGovernance, x => x["GovernanceDW"]);
        }

        private static void RegisterCollections(
            Container container,
            Dictionary<string, string> currentConnectionStrings,
            Dictionary<string, string> syncConnectionStrings,
            BusinessResearchProduct key,
            Func<Dictionary<string, string>, string> getDatabaseName)
        {
            foreach (var entityType in ResearchTypeDictionary[key])
            {
                var collectionType = typeof(ValidationMongoCollection<>).MakeGenericType(entityType);
                var type = entityType;

                if (!_allreadyRegistredTypes.Contains(type))
                {
                    _allreadyRegistredTypes.Add(type);
                    container.Register(
                      collectionType,
                      () =>
                          {
                              var method =
                                  typeof(ValidationMongoCollectionFactory).GetMethod(
                                      "GetCollectionFromDatabase",
                                      BindingFlags.Public | BindingFlags.Static).MakeGenericMethod(type);
                              return method.Invoke(
                                  null,
                                  new object[] { syncConnectionStrings, currentConnectionStrings, getDatabaseName, null });
                          });
                }
            }
        }

        private static void RegisterDataHandlers(this Container container)
        {
            var handlersTypes = new List<Type>();

            foreach (var researchType in ResearchTypeDictionary.Keys)
            {
                foreach (var type in ResearchTypeDictionary[researchType])
                {
                    Type handlerType;

                    switch (researchType)
                    {
                        case BusinessResearchProduct.Comprehensive:
                            handlerType = type.ImplementsInterface(typeof(Sustainalytics.Entities.IHasCompanyId))
                                ? typeof(ComprehensiveHasCompanyIdProfileRestoreHandler<>).MakeGenericType(type)
                                : typeof(ComprehensiveEntityProfileRestoreProfileRestoreHandler<>).MakeGenericType(type);
                            break;

                        case BusinessResearchProduct.Core:
                            handlerType = type.ImplementsInterface(typeof(Sustainalytics.Entities.IHasCompanyId))
                                ? typeof(CoreHasCompanyIdProfileRestoreHandler<>).MakeGenericType(type)
                                : typeof(CoreEntityProfileRestoreProfileRestoreHandler<>).MakeGenericType(type);
                            break;

                        case BusinessResearchProduct.CorporateGovernance:
                            handlerType = type.ImplementsInterface(typeof(Sustainalytics.Entities.IHasCompanyId))
                                ? typeof(GovernanceHasCompanyIdProfileRestoreHandler<>).MakeGenericType(type)
                                : typeof(GovernanceEntityProfileRestoreProfileRestoreHandler<>).MakeGenericType(type);
                            break;

                        case BusinessResearchProduct.Events:
                            handlerType = type.ImplementsInterface(typeof(Sustainalytics.Entities.IHasCompanyId))
                                ? typeof(EventsHasCompanyIdProfileRestoreHandler<>).MakeGenericType(type)
                                : typeof(EventsEntityProfileRestoreProfileRestoreHandler<>).MakeGenericType(type);
                            break;
                        default:
                            throw new ArgumentException("ProfileRestore ResearchType is not known");
                    }

                    handlersTypes.Add(handlerType);
                }
            }

            container.RegisterCollection(typeof(IProfileRestoreHandler), handlersTypes);
        }

        private static void RegisterLoggers(this Container container, SyncContext context)
        {
            container.Register(typeof(ILogger), () => new SyncContextLogger(context), Lifestyle.Singleton);
            container.RegisterDecorator<IProfileRestoreHandler, SyncLogProfileRestoreHandlerDecorator>(Lifestyle.Singleton);
            container.RegisterInitializer<SyncLogProfileRestoreHandlerDecorator>(logger => { logger.SetLoggingOutput(context.Log); });
        }
    }
}
